﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Threading;
using System.Windows;
using System.Threading;
using IceMan.Domain;

namespace IceMan.ViewModels
{
    public class StateManager : BindableBase
    {
        /// <summary>
        /// 当前状态
        /// </summary>
        private StatyeType state = StatyeType.Idle;
        public StatyeType State
        {
            get { return state; }
            set { SetProperty(ref state, value); }
        }

        /// <summary>
        /// 空闲状态显示的内容
        /// </summary>
        private static string idleDisplayState = "空闲中";



        private string window1DisplayState = idleDisplayState;
        public string Window1DisplayState
        {
            get { return window1DisplayState; }
            set { SetProperty(ref window1DisplayState, value); }
        }

        private string window2DisplayState = idleDisplayState;
        public string Window2DisplayState
        {
            get { return window2DisplayState; }
            set { SetProperty(ref window2DisplayState, value); }
        }

        private string replaceWindowState;
        public string ReplaceWindowState
        {
            get { return replaceWindowState; }
            set { SetProperty(ref replaceWindowState, value); }
        }

        private StateContainer makeFirstStates;
        private StateContainer makeSecondStates;
        private StateContainer replaceMaterialStates;

        public StateManager(IStateStore stateStore, Config config)
        {
            makeFirstStates = new StateContainer(new List<IMaterialType>
            {
                Strawberry.Default,Chocolates.Default,IceCream.Default
            }, 5, WorkType.Window1);

            makeSecondStates = new StateContainer(new List<IMaterialType>
            {
                 IceCream.Default, Strawberry.Default,Chocolates.Default
            }, 8, WorkType.Window2);

            replaceMaterialStates = new StateContainer(new List<IMaterialType>
            {
                IceCream.Default , Strawberry.Default , Chocolates.Default
            }, 5, WorkType.ReplaceMaterial);


            makeFisrt = new DelegateCommand(ExecuteMakeFirst, CanExecuteMakeFirst);
            //makeFisrt.ObservesProperty(() => makeFirstStates.IsRunning);

            makeSecond = new DelegateCommand(ExecuteMakeSecond, CanExecuteMakeSecond);
            //makeSecond.ObservesProperty(() => makeSecondStates.IsRunning);


            replaceMaterial = new DelegateCommand(ExecuteReplaceMaterial, CanExecuteReplaceMaterial);
            //replaceMaterial.ObservesProperty(() => replaceMaterialStates.IsRunning);

            this.stateStore = stateStore;
            this.config = config;

            InitState();
        }

        public Chocolates Chocolates { get; set; } = Chocolates.Default;
        public IceCream IceCream { get; set; } = IceCream.Default;
        public Strawberry Strawberry { get; set; } = Strawberry.Default;
        private void InitState()
        {
            try
            {
                MainState = stateStore.Load();

                MainState.InitRef(config, stateStore);

                MainState.InitStateByLog(makeFirstStates, makeSecondStates, replaceMaterialStates);

                Window1DisplayState = makeFirstStates.GetStateInfo();
                Window2DisplayState = makeSecondStates.GetStateInfo();
                ReplaceWindowState = replaceMaterialStates.GetStateInfo();

                RaiseCmdChange();
            }
            catch (Exception ex)
            {
            }

        }

        public MainState MainState { get; private set; }

        /// <summary>
        /// 切换到下一个状态 成功时，使用此方法更新页面状态并记录修改日志
        /// </summary>
        /// <param name="stateContainer"></param>
        /// <exception cref="InvalidOperationException"></exception>
        private async ValueTask NextStateAction(StateContainer stateContainer)
        {
            // 记录步骤完成日志
            switch (stateContainer.WorkType)
            {
                case WorkType.Window1:
                    Window1DisplayState = stateContainer.GetStateInfo();
                    await stateContainer.DoWork();
                    MainState.AddWindow_1Log(stateContainer.CurrentState, false);
                    break;
                case WorkType.Window2:
                    Window2DisplayState = stateContainer.GetStateInfo();
                    await stateContainer.DoWork();
                    MainState.AddWindow_2Log(stateContainer.CurrentState, false);
                    break;
                case WorkType.ReplaceMaterial:
                    ReplaceWindowState = stateContainer.GetStateInfo();
                    await stateContainer.DoWork();
                    MainState.AddReplaceMaterialLog(stateContainer.CurrentState, false);
                    break;
                default:
                    throw new InvalidOperationException($"当前状态类型无法追加日志{stateContainer.WorkType}");
            }
        }

        private void RaiseCmdChange()
        {
            makeFisrt.RaiseCanExecuteChanged();
            makeSecond.RaiseCanExecuteChanged();
            replaceMaterial.RaiseCanExecuteChanged();

            // 取消按钮是否显示
            Window1CancelButtonVisibility = makeFirstStates.IsInitState ? Visibility.Collapsed : Visibility.Visible;
            Window2CancelButtonVisibility = makeSecondStates.IsInitState ? Visibility.Collapsed : Visibility.Visible;
            ReplaceWindowCancelButtonVisibility = replaceMaterialStates.IsInitState ? Visibility.Collapsed : Visibility.Visible;
        }
        private async ValueTask StartNewWork(StateContainer stateContainer)
        {
            while (stateContainer.NextState())
            {
                RaiseCmdChange();
                await NextStateAction(stateContainer);
            }

            // 添加完成日志
            switch (stateContainer.WorkType)
            {
                case WorkType.Window1:
                    MainState.AddWindow_1Log(null, true);
                    Window1DisplayState = idleDisplayState;
                    break;
                case WorkType.Window2:
                    MainState.AddWindow_2Log(null, true);
                    Window2DisplayState = idleDisplayState;
                    break;
                case WorkType.ReplaceMaterial:
                    MainState.AddReplaceMaterialLog(null, true);
                    ReplaceWindowState = idleDisplayState;
                    break;
                default:
                    throw new InvalidOperationException($"当前状态类型无法追加日志{stateContainer.WorkType}");
            }
            RaiseCmdChange();
        }
        /// <summary>
        /// 进入窗口1制作状态
        /// </summary>
        public void StartMakeFirst()
        {
            if (makeFirstStates.IsRunning)
            {
                throw new InvalidOperationException("正在窗口1制作中，无法重复进入此状态");
            }

            _ = StartNewWork(makeFirstStates);
        }
        /// <summary>
        /// 进入窗口2制作状态
        /// </summary>
        public void StartMakeSecond()
        {
            if (makeSecondStates.IsRunning)
            {
                throw new InvalidOperationException("正在窗口2制作中，无法重复进入此状态");
            }

            _ = StartNewWork(makeSecondStates);
        }

        /// <summary>
        /// 进入更换材料状态
        /// </summary>
        public void StartReplaceMaterial()
        {
            if (replaceMaterialStates.IsRunning)
            {
                throw new InvalidOperationException("正在更换材料，无法重复进入此状态");
            }

            _ = StartNewWork(replaceMaterialStates);
        }

        public bool IsMaterialEnough => MainState.MaterialCount > 0;

        #region 命令

        /// <summary>
        /// 窗口1制作命令
        /// </summary>

        private DelegateCommand makeFisrt;
        public DelegateCommand MakeFirst =>
            makeFisrt ?? (makeFisrt = new DelegateCommand(ExecuteMakeFirst, CanExecuteMakeFirst));

        void ExecuteMakeFirst()
        {
            StartMakeFirst();
        }

        bool CanExecuteMakeFirst()
        {
            // 材料充足，且当前窗口不在运行状态
            return !replaceMaterialStates.IsRunning && !makeFirstStates.IsRunning && IsMaterialEnough;
        }


        /// <summary>
        /// 窗口2制作命令
        /// </summary>

        private DelegateCommand makeSecond;
        public DelegateCommand MakeSecond =>
            makeSecond ?? (makeSecond = new DelegateCommand(ExecuteMakeSecond, CanExecuteMakeSecond));

        void ExecuteMakeSecond()
        {
            StartMakeSecond();
        }

        bool CanExecuteMakeSecond()
        {
            // 材料充足，且当前窗口不在运行状态
            return !replaceMaterialStates.IsRunning && !makeSecondStates.IsRunning && IsMaterialEnough;
        }

        private DelegateCommand replaceMaterial;
        private readonly IStateStore stateStore;
        private readonly Config config;

        public DelegateCommand ReplaceMaterial =>
            replaceMaterial ?? (replaceMaterial = new DelegateCommand(ExecuteReplaceMaterial, CanExecuteReplaceMaterial));

        void ExecuteReplaceMaterial()
        {
            StartReplaceMaterial();
        }

        bool CanExecuteReplaceMaterial()
        {
            // 两个窗口都没有运行，则可以更换材料
            return !replaceMaterialStates.IsRunning && !makeSecondStates.IsRunning && !makeFirstStates.IsRunning;
        }


        private DelegateCommand cancelWindow1;
        public DelegateCommand CancelWindow1 =>
            cancelWindow1 ?? (cancelWindow1 = new DelegateCommand(ExecuteCancelWindow1));

        void ExecuteCancelWindow1()
        {
            makeFirstStates.ResetInitState();
            MainState.AddWindow_1Log(null, true);
            Window1DisplayState = makeFirstStates.GetStateInfo();

            RaiseCmdChange();
        }

        private DelegateCommand cancelWindow2;
        public DelegateCommand CancelWindow2 =>
            cancelWindow2 ?? (cancelWindow2 = new DelegateCommand(ExecuteCancelWindow2));

        void ExecuteCancelWindow2()
        {
            makeSecondStates.ResetInitState();
            MainState.AddWindow_2Log(null, true);
            Window2DisplayState = makeSecondStates.GetStateInfo();


            RaiseCmdChange();
        }

        private DelegateCommand cancelReplaceMaterial;
        public DelegateCommand CancelReplaceMaterial =>
            cancelReplaceMaterial ?? (cancelReplaceMaterial = new DelegateCommand(ExecuteCancelReplaceMaterial));

        void ExecuteCancelReplaceMaterial()
        {
            replaceMaterialStates.ResetInitState();
            MainState.AddReplaceMaterialLog(null, true);
            ReplaceWindowState = replaceMaterialStates.GetStateInfo();

            RaiseCmdChange();
        }

        private Visibility window2CancelButtonVisibility;
        public Visibility Window2CancelButtonVisibility
        {
            get { return window2CancelButtonVisibility; }
            set { SetProperty(ref window2CancelButtonVisibility, value); }
        }

        private Visibility window1CancelButtonVisibility;
        public Visibility Window1CancelButtonVisibility
        {
            get { return window1CancelButtonVisibility; }
            set { SetProperty(ref window1CancelButtonVisibility, value); }
        }

        private Visibility replaceWindowCancelButtonVisibility;
        public Visibility ReplaceWindowCancelButtonVisibility
        {
            get { return replaceWindowCancelButtonVisibility; }
            set { SetProperty(ref replaceWindowCancelButtonVisibility, value); }
        }
        #endregion
    }
}
